View Javadoc

1   /*
2    * Copyright (C) 1998-2000 Semiotek Inc.  All Rights Reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted under the terms of either of the following
6    * Open Source licenses:
7    *
8    * The GNU General Public License, version 2, or any later version, as
9    * published by the Free Software Foundation
10   * (http://www.fsf.org/copyleft/gpl.html);
11   *
12   *  or
13   *
14   * The Semiotek Public License (http://webmacro.org/LICENSE.)
15   *
16   * This software is provided "as is", with NO WARRANTY, not even the
17   * implied warranties of fitness to purpose, or merchantability. You
18   * assume all risks and liabilities associated with its use.
19   *
20   * See www.webmacro.org for more information on the WebMacro project.
21   */
22  
23  package org.webmacro.directive;
24  
25  import org.webmacro.directive.Directive.ArgDescriptor;
26  import org.webmacro.directive.Directive.Subdirective;
27  
28  /***
29   * Each directive needs a DirectiveDescriptor to describe how it
30   * should be parsed and built by the parser.  The directive descriptor
31   * identifies the directive's name, the class of the underlying concrete
32   * directive object, a list of directive argument descriptors, and a list
33   * of subdirective descriptors.
34   *
35   * If the directive does not specify the class, the directive provider will
36   * fill it in automatically.
37   *
38   * The args field is an array of Directive.ArgDescriptor objects.  There
39   * are static nested classes within Directive for each type of argument
40   * -- Condition, LValue, RValue, Keyword, Punctuation, Block, LiteralBlock,
41   * and special argument descriptors for OptionalGroup and OptionalChoice.
42   * These allow the directive writer to specify
43   * a flexible syntax for directive arguments.
44   * Each directive can have a set of subdirectives, and each subdirective
45   * can have its own argument list.  Subdirectives can be required, optional,
46   * or optional-repeating (multiple subdirectives of the same kind, like
47   * #elseif.)  There are constructors for Subdirective in the Directive module
48   * as well.
49   * @author Brian Goetz
50   */
51  
52  public final class DirectiveDescriptor
53  {
54  
55      public String name;
56      public Class dirClass;
57      public ArgDescriptor[] args;
58      public Subdirective[] subdirectives;
59  
60      public boolean valid = false,
61      hasBreakingSubdirectives = false;
62  
63      public DirectiveDescriptor (String name,
64                                  Class dirClass,
65                                  ArgDescriptor[] args,
66                                  Subdirective[] subdirectives)
67      {
68          this.name = name;
69          this.dirClass = dirClass;
70          this.args = args;
71          this.subdirectives = subdirectives;
72  
73          completeArgs(this.args);
74          valid = validateArgs(this.args);
75  
76          if (subdirectives != null)
77          {
78              for (int i = 0; i < this.subdirectives.length; i++)
79              {
80                  completeArgs(this.subdirectives[i].args);
81                  valid &= validateArgs(this.subdirectives[i].args);
82                  if (this.subdirectives[i].isBreaking)
83                      hasBreakingSubdirectives = true;
84              }
85          }
86      }
87  
88      /***
89       * Determines the index of the next argument following a given argument.
90       * May return an index > args.length.
91       */
92      private static int nextArg (ArgDescriptor[] args, int i)
93      {
94          if (args[i].type == Directive.ArgType_GROUP
95                  || args[i].type == Directive.ArgType_CHOICE)
96          {
97              int k = i + 1;
98              for (int j = 0; j < args[i].subordinateArgs; j++)
99                  k = nextArg(args, k);
100             return k;
101         }
102         else
103             return i + 1;
104     }
105 
106     /***
107      * Set the nextArg, children[] fields as necessary
108      */
109     private static void completeArgs (ArgDescriptor[] args)
110     {
111         int j, k;
112 
113         for (int i = 0; i < args.length; i++)
114             args[i].nextArg = nextArg(args, i);
115 
116         for (int i = 0; i < args.length; i++)
117         {
118             switch (args[i].type)
119             {
120                 case Directive.ArgType_GROUP:
121                 case Directive.ArgType_CHOICE:
122                     args[i].children = new int[args[i].subordinateArgs];
123                     for (j = 0, k = i + 1; j < args[i].subordinateArgs; j++)
124                     {
125                         args[i].children[j] = k;
126                         k = args[k].nextArg;
127                     }
128                     break;
129 
130                 default:
131                     break;
132             }
133         }
134     }
135 
136     /***
137      * Make sure that the structure of the arguments list is valid.
138      * This means that
139      *   GROUP arguments begin with a keyword, not optional
140      *   Each of the children of a CHOICE argument is an OPTIONAL GROUP
141      */
142     private static boolean validateArgs (ArgDescriptor[] args)
143     {
144         boolean valid = true;
145         for (int i = 0; i < args.length; i++)
146         {
147             if (args[i].type == Directive.ArgType_GROUP)
148             {
149                 if (args[i].subordinateArgs == 0)
150                     valid = false;
151                 else if ((args[args[i].children[0]].type != Directive.ArgType_KEYWORD
152                         && args[args[i].children[0]].type != Directive.ArgType_ASSIGN)
153                         || args[args[i].children[0]].optional)
154                     valid = false;
155             }
156             else if (args[i].type == Directive.ArgType_CHOICE)
157             {
158                 for (int j = 0; j < args[i].subordinateArgs; j++)
159                 {
160                     if (args[args[i].children[j]].type != Directive.ArgType_GROUP
161                             || !args[args[i].children[j]].optional)
162                         valid = false;
163                 }
164             }
165         }
166 
167         return valid;
168     }
169 }